package kbl.language;

import java.util.Stack;

public class RegularExpressionParser {
	String reString;
	RegularExpression postfixRE;
	Stack<RElement> reStack = new Stack<RElement>();

	public RegularExpressionParser(String reStr) {
		this.reString = reStr;
	}

	public RegularExpressionParser() {

	}

	public RegularExpression doReversePolishNotation(String re) {
		this.reString = re;
		return doReversePolishNotation();
	}

	public String postfixRE(String re) {
		return doReversePolishNotation(re).toString();
	}

	public RegularExpression doReversePolishNotation() {
		postfixRE = new RegularExpression(reString);
		char[] rea = reString.toCharArray();
		for (int i = 0; i < rea.length; i++) {
			RElement input = new RElement(rea[i]);
			CommonType it = input.getOperator();
			switch (it) {
			case LParenthesis:
				reStack.push(input);
				break;
			case RParenthesis:
				boolean lp = false;
				// Until the token at the top of the stack is a left
				// parenthesis,
				// pop operators off the stack onto the output queue
				while (!reStack.empty()) {
					RElement o = reStack.pop();
					if (o.getOperator().isLParenthsis()) {
						lp = true;
						break;
					} else {
						postfixRE.add(o);
					}
				}
				// If the stack runs out without finding a left parenthesis,
				// then there are mismatched parentheses.
				if (!lp) {
					System.err.println("Error: parentheses mismatched at " + i
							+ ".");
					System.exit(1);
				}
				break;
			case CharacterClass:
				CharacterClass cc = new CharacterClass();
				cc.addRange(new CharRange(Character.MIN_VALUE,
						Character.MAX_VALUE));
				postfixRE.add(new RElement(cc));
				break;
			case LBracket:
				i = i + parseCharacterClass(rea, i + 1);
				break;
			case RBracket:
				System.err.println("Error: Square brackets mismatched.");
				System.exit(1);
			case Asterisk:
			case Plus:
			case QuestionMark:
			case Alternate:
				add2Queue(it);
				// push op1 onto the stack.
				reStack.push(input);
				break;
			case Escape:
				i = i + parseEscape(rea, i);
				break;
			default: // Token
				postfixRE.add(input);
				break;
			}
			if (i < rea.length - 1) {
				doConcatenation(it,
						new RElement(rea[i + 1]).getOperator());
			}
		}
		// When there are no more tokens to read:
		// While there are still operator tokens in the stack:
		while (!reStack.empty()) {
			RElement o = reStack.pop();
			if (o.getOperator().isParenthsis()) {
				System.err.println("Error: parentheses mismatched.");
				System.exit(1);
			}
			postfixRE.add(o);
		}

		return postfixRE;
	}

	private void add2Queue(CommonType it) {
		while (!reStack.empty()) {
			CommonType o2 = reStack.peek().getOperator();
			// While there is an operator token, o2 at the top
			// of the stack, op1 is left-associative and its
			// precedence is less than or equal to that of op2,
			// or op1 is right-associative and its precedence is
			// less than that of op2,
			if (o2.isTarget2Queue(it)) {
				// Pop o2 off the stack, onto the output queue;
				postfixRE.add(reStack.pop());
			} else {
				break;
			}
		}
	}

	private int parseEscape(char[] rea, int i) {
		switch (rea[i + 1]) {
		case '\\':
			postfixRE.add(new RElement('\\'));
			break;
		case 't':
			postfixRE.add(new RElement('\t'));
			break;
		case 'n':
			postfixRE.add(new RElement('\n'));
			break;
		case 'r':
			postfixRE.add(new RElement('\r'));
			break;
		default:
			postfixRE.add(new RElement(CommonType.Literal ,rea[i + 1]));
			break;
		}
		return 1;
	}

	private int parseCharacterClass(char[] rea, int i) {
		CharacterClass cc = new CharacterClass();
		boolean negated = false;
		int t = 1;
		if (rea[i] == '^') {
			negated = true;
			t = t + 1;
			i = i + 1;
		}
		while ((i < rea.length - 1) && rea[i] != ']') {
			if (rea[i + 1] == '-') {
				if (rea[i + 2] < rea[i] || rea[i + 2] == ']') {
					System.err.println("Error: Bad range.");
					System.exit(1);
				}
				cc.addRange(new CharRange(rea[i], rea[i + 2]));
				t = t + 3;
				i = i + 3;
			} else {
				if (rea[i] == '\\') {
					switch (rea[i + 1]) {
					case '\\':
						cc.addRange(new CharRange('\\', '\\'));
						break;
					case 't':
						cc.addRange(new CharRange('\t', '\t'));
						break;
					case 'n':
						cc.addRange(new CharRange('\n', '\n'));
						break;
					case 'r':
						cc.addRange(new CharRange('\r', '\r'));
						break;
					default:
						cc.addRange(new CharRange(rea[i + 1], rea[i + 1]));
						break;
					}
					t = t + 2;
					i = i + 2;
				} else {
					cc.addRange(new CharRange(rea[i], rea[i]));
					t++;
					i++;
				}
			}
		}
		if (rea[i] != ']') {
			System.err.println("Error: Square brackets mismatched.");
			System.exit(1);
		}
		if (negated) {
			cc.negate();
		}
		postfixRE.add(new RElement(cc));
		return t;
	}

	private void doConcatenation(CommonType i1, CommonType i2) {
		// current is Unary or Token And next is '(' or Token
		if (i1.isUnaryOrLiteral() && i2.isLParenthesisOrLiteral()) {
			add2Queue(CommonType.Concatenate);
			reStack.push(new RElement(CommonType.Concatenate));
		}
	}

}